home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / gnome_sudoku / sudoku_maker.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  20.3 KB  |  683 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import sys
  5. import os
  6. import shutil
  7. import os.path as os
  8. import sudoku
  9. import random
  10. import pickle
  11. import time
  12. import pausable
  13. import threading
  14. import saver
  15. from gettext import gettext as _
  16. from defaults import *
  17.  
  18. class SudokuGenerator:
  19.     '''A class to generate new Sudoku Puzzles.'''
  20.     
  21.     def __init__(self, start_grid = None, clues = 2, group_size = 9):
  22.         self.generated = []
  23.         self.clues = clues
  24.         self.all_coords = []
  25.         self.group_size = group_size
  26.         for x in range(self.group_size):
  27.             for y in range(self.group_size):
  28.                 self.all_coords.append((x, y))
  29.             
  30.         
  31.         if start_grid:
  32.             self.start_grid = sudoku.SudokuGrid(start_grid)
  33.         else:
  34.             
  35.             try:
  36.                 self.start_grid = self.generate_grid()
  37.             except:
  38.                 self.start_grid = self.generate_grid()
  39.  
  40.         self.puzzles = []
  41.         self.rated_puzzles = []
  42.  
  43.     
  44.     def average_difficulty(self):
  45.         difficulties = [ i[1].value for i in self.rated_puzzles ]
  46.         if difficulties:
  47.             return sum(difficulties) / len(difficulties)
  48.  
  49.     
  50.     def generate_grid(self):
  51.         self.start_grid = sudoku.SudokuSolver(verbose = False, group_size = self.group_size)
  52.         self.start_grid.solve()
  53.         return self.start_grid
  54.  
  55.     
  56.     def reflect(self, x, y, axis = 1):
  57.         upper = self.group_size - 1
  58.         x = upper - y
  59.         y = upper - x
  60.         return (y, x)
  61.  
  62.     
  63.     def make_symmetric_puzzle(self):
  64.         nclues = self.clues / 2
  65.         buckshot = set(random.sample(self.all_coords, nclues))
  66.         new_puzzle = sudoku.SudokuGrid(verbose = False, group_size = self.group_size)
  67.         reflections = set()
  68.         for x, y in buckshot:
  69.             reflection = self.reflect(x, y)
  70.             if reflection:
  71.                 nclues += 1
  72.                 reflections.add(reflection)
  73.                 continue
  74.         
  75.         buckshot = buckshot | reflections
  76.         remaining_coords = set(self.all_coords) - set(buckshot)
  77.         while len(buckshot) < self.clues:
  78.             coord = random.sample(remaining_coords, 1)[0]
  79.             buckshot.add(coord)
  80.             reflection = self.reflect(*coord)
  81.             if reflection:
  82.                 buckshot.add(reflection)
  83.             
  84.             remaining_coords = remaining_coords - buckshot
  85.         return self.make_puzzle_from_coords(buckshot)
  86.  
  87.     
  88.     def make_puzzle(self):
  89.         buckshot = random.sample(self.all_coords, self.clues)
  90.         while buckshot in self.generated:
  91.             buckshot = random.sample(self.all_coords, self.clues)
  92.         return self.make_puzzle_from_coords(buckshot)
  93.  
  94.     
  95.     def make_puzzle_from_coords(self, buckshot):
  96.         new_puzzle = sudoku.SudokuGrid(verbose = False, group_size = self.group_size)
  97.         self.generated.append(set(buckshot))
  98.         for x, y in buckshot:
  99.             new_puzzle.add(x, y, self.start_grid._get_(x, y))
  100.         
  101.         self.puzzles.append(new_puzzle)
  102.         return new_puzzle
  103.  
  104.     
  105.     def make_puzzle_by_boxes(self, skew_by = 0, max_squares = None):
  106.         '''Make a puzzle paying attention to evenness of clue
  107.         distribution.
  108.  
  109.         If skew_by is 0, we distribute our clues as evenly as possible
  110.         across boxes.  If skew by is 1.0, we make the distribution of
  111.         clues as uneven as possible. In other words, if we had 27
  112.         boxes for a 9x9 grid, a skew_by of 0 would put exactly 3 clues
  113.         in each 3x3 grid whereas a skew_by of 1.0 would completely
  114.         fill 3 3x3 grids with clues.
  115.  
  116.         We believe this skewing may have something to do with how
  117.         difficult a puzzle is to solve. By toying with the ratios,
  118.         this method may make it considerably easier to generate
  119.         difficult or easy puzzles.
  120.         '''
  121.         nboxes = len(self.start_grid.boxes)
  122.         if not max_squares:
  123.             max_squares = self.clues / nboxes
  124.             max_squares += int((nboxes - max_squares) * skew_by)
  125.         
  126.         clued = 0
  127.         nclues = []
  128.         for n in range(nboxes):
  129.             minimum = (self.clues - clued) / (nboxes - n)
  130.             if max_squares < minimum:
  131.                 cls = minimum
  132.             else:
  133.                 cls = int(max_squares)
  134.             clues = max_squares
  135.             if clues > self.clues - clued:
  136.                 clues = self.clues - clued
  137.             
  138.             nclues.append(int(clues))
  139.             clued += clues
  140.             if skew_by:
  141.                 max_squares = round(max_squares * skew_by)
  142.                 continue
  143.         
  144.         random.shuffle(nclues)
  145.         buckshot = []
  146.         for i in range(nboxes):
  147.             if nclues[i]:
  148.                 buckshot.extend(random.sample(self.start_grid.box_coords[i], nclues[i]))
  149.                 continue
  150.         
  151.         return self.make_puzzle_from_coords(buckshot)
  152.  
  153.     
  154.     def assess_difficulty(self, sudoku_grid):
  155.         
  156.         try:
  157.             solver = sudoku.SudokuRater(sudoku_grid, verbose = False, group_size = self.group_size)
  158.             d = solver.difficulty()
  159.             self.rated_puzzles.append((sudoku_grid, d))
  160.             return d
  161.         except:
  162.             print 'Impossible!'
  163.             print 'Puzzle was:'
  164.             print solver.virgin
  165.             print 'Solution: ', self.start_grid
  166.             print 'Puzzle foobared in following state:', solver
  167.             raise 
  168.  
  169.  
  170.     
  171.     def is_unique(self, sudoku_grid):
  172.         '''If puzzle is unique, return its difficulty.
  173.  
  174.         Otherwise, return None.'''
  175.         solver = sudoku.SudokuRater(sudoku_grid, verbose = False, group_size = self.group_size)
  176.         if solver.has_unique_solution():
  177.             return solver.difficulty()
  178.         return None
  179.  
  180.     
  181.     def generate_puzzle_for_difficulty(self, lower_target = 0.3, upper_target = 0.5, max_tries = 100, by_box = False, by_box_kwargs = { }):
  182.         for i in range(max_tries):
  183.             if by_box:
  184.                 puz = self.make_puzzle_by_boxes(**by_box_kwargs)
  185.             else:
  186.                 puz = self.make_puzzle()
  187.             d = self.assess_difficulty(puz.grid)
  188.             if d:
  189.                 if not lower_target or d.value > lower_target:
  190.                     if not upper_target or d.value < upper_target:
  191.                         return (puz, d)
  192.                 return (None, None)
  193.             return d
  194.  
  195.     
  196.     def make_unique_puzzle(self, symmetrical = True, strict_number_of_clues = False):
  197.         if symmetrical:
  198.             puz = self.make_symmetric_puzzle()
  199.         else:
  200.             puz = make_puzzle()
  201.         diff = False
  202.         if self.clues > 10:
  203.             clues = self.clues - 8
  204.         else:
  205.             clues = 2
  206.         while None:
  207.             solver = sudoku.SudokuRater(puz.grid, verbose = False, group_size = self.group_size)
  208.             if solver.has_unique_solution():
  209.                 diff = solver.difficulty()
  210.                 break
  211.             
  212.             crumb = solver.breadcrumbs[-1]
  213.             fill_in = [
  214.                 (crumb.x, crumb.y)]
  215.             reflection = self.reflect(crumb.x, crumb.y)
  216.             if reflection:
  217.                 fill_in.append(reflection)
  218.             
  219.             for x, y in fill_in:
  220.                 solver.virgin._set_(x, y, self.start_grid._get_(x, y))
  221.             
  222.             puz = sudoku.SudokuGrid(solver.virgin.grid, verbose = False)
  223.             clues += len(fill_in)
  224.             if strict_number_of_clues == True and clues > self.clues:
  225.                 return None
  226.             continue
  227.             if strict_number_of_clues:
  228.                 changed = False
  229.                 while clues < self.clues:
  230.                     x = random.randint(0, 8)
  231.                     y = random.randint(0, 8)
  232.                     while puz._get_(x, y):
  233.                         x = random.randint(0, 8)
  234.                         y = random.randint(0, 8)
  235.                         continue
  236.                         clues > self.clues
  237.                     puz._set_(x, y, self.start_grid._get_(x, y))
  238.                     clues += 1
  239.                     reflection = self.reflect(x, y)
  240.                     if reflection:
  241.                         puz._set_(x, y, self.start_grid._get_(x, y))
  242.                         clues += 1
  243.                     
  244.                     changed = True
  245.                 if changed:
  246.                     diff = sudoku.SudokuRater(puz.grid, verbose = False, group_size = self.group_size).difficulty()
  247.                 
  248.             
  249.         return (puz, diff)
  250.  
  251.     
  252.     def make_unique_puzzles(self, n = 10, ugargs = { }):
  253.         ug = self.unique_generator(**ugargs)
  254.         ret = []
  255.         for i in range(n):
  256.             ret.append(ug.next())
  257.         
  258.         return ret
  259.  
  260.     
  261.     def unique_generator(self, symmetrical = True, strict_number_of_clues = False, by_box = False, by_box_kwargs = { }):
  262.         while None:
  263.             result = self.make_unique_puzzle(symmetrical = symmetrical, strict_number_of_clues = strict_number_of_clues)
  264.             if result:
  265.                 yield result
  266.                 continue
  267.             continue
  268.             return None
  269.  
  270.     
  271.     def generate_puzzles(self, n = 10, symmetrical = True, by_box = False, by_box_kwargs = { }):
  272.         ret = []
  273.         for i in range(n):
  274.             if symmetrical:
  275.                 puz = self.make_symmetric_puzzle()
  276.             elif by_box:
  277.                 puz = self.make_puzzle_by_boxes(**by_box_kwargs)
  278.             else:
  279.                 puz = self.make_puzzle()
  280.             
  281.             try:
  282.                 d = self.assess_difficulty(puz.grid)
  283.             except:
  284.                 raise 
  285.  
  286.             if d:
  287.                 ret.append((puz, d))
  288.                 continue
  289.         
  290.         ret.sort((lambda a, b: if not a[1].value > b[1].value or 1:
  291. if not a[1].value < b[1].value or -1:
  292. pass0))
  293.         return ret
  294.  
  295.  
  296.  
  297. class InterruptibleSudokuGenerator(SudokuGenerator):
  298.     
  299.     def __init__(self, *args, **kwargs):
  300.         self.paused = False
  301.         self.terminated = False
  302.         SudokuGenerator.__init__(self, *args, **kwargs)
  303.  
  304.     
  305.     def work(self, *args, **kwargs):
  306.         self.unterminate()
  307.         SudokuGenerator(self, *args, **kwargs)
  308.  
  309.  
  310. pausable.make_pausable(InterruptibleSudokuGenerator)
  311.  
  312. class SudokuMaker:
  313.     '''A class to create unique, symmetrical sudoku puzzles.'''
  314.     
  315.     def __init__(self, generator_args = {
  316.         'clues': 27,
  317.         'group_size': 9 }, puzzle_maker_args = {
  318.         'symmetrical': True }, batch_size = 5, pickle_to = os.path.join(DATA_DIR, 'puzzles')):
  319.         self.pickle_to = pickle_to
  320.         self.paused = False
  321.         self.terminated = False
  322.         self.generator_args = generator_args
  323.         self.puzzle_maker_args = puzzle_maker_args
  324.         self.batch_size = batch_size
  325.         self.load()
  326.         self.all_puzzles = { }
  327.         self.played = self.get_pregenerated('finished')
  328.         self.n_available_sudokus = {
  329.             'easy': None,
  330.             'medium': None,
  331.             'hard': None,
  332.             'very hard': None }
  333.  
  334.     
  335.     def load(self):
  336.         
  337.         try:
  338.             os.makedirs(self.pickle_to)
  339.         except os.error:
  340.             e = None
  341.             if e.errno != errno.EEXIST:
  342.                 return None
  343.         except:
  344.             e.errno != errno.EEXIST
  345.  
  346.         for cat in sudoku.DifficultyRating.categories:
  347.             source = os.path.join(os.path.join(PUZZLE_DIR), cat.replace(' ', '_'))
  348.             target = os.path.join(self.pickle_to, cat.replace(' ', '_'))
  349.             if not os.path.exists(target):
  350.                 
  351.                 try:
  352.                     shutil.copy(source, target)
  353.                 print 'Problem copying base puzzles'
  354.                 print 'Attempted to copy from %s to %s' % (source, target)
  355.  
  356.                 continue
  357.         
  358.  
  359.     
  360.     def get_pregenerated(self, difficulty):
  361.         fname = os.path.join(self.pickle_to, difficulty.replace(' ', '_'))
  362.         
  363.         try:
  364.             lines = file(fname).readlines()
  365.         except IOError:
  366.             e = None
  367.             if e.errno != errno.ENOENT:
  368.                 print "Error reading pregenerated puzzles for difficulty '%s': %s" % (difficulty, e.strerror)
  369.             
  370.             return []
  371.  
  372.         return [ line.strip() for line in lines ]
  373.  
  374.     
  375.     def get_new_puzzle(self, difficulty, new = True):
  376.         '''Return puzzle with difficulty near difficulty.
  377.  
  378.         If new is True, we return only unplayed puzzles.
  379.         Return a tuple containing a new puzzle and difficulty object.
  380.         '''
  381.         val_cat = sudoku.get_difficulty_category(difficulty)
  382.         if not val_cat:
  383.             print 'WARNING, no val cat for difficulty:', difficulty
  384.             if val_cat > 1:
  385.                 val_cat = 'very hard'
  386.             else:
  387.                 val_cat = 'easy'
  388.         
  389.         puzzles = []
  390.         lines = self.get_pregenerated(val_cat)
  391.         closest = (0x9184E72A000L, None)
  392.         for l in lines:
  393.             if len(l) == 0:
  394.                 print 'Warning: file %s contains an empty line' % fname
  395.                 continue
  396.             
  397.             if not l.find('\t') >= 0:
  398.                 print 'Warning: line "%s" of file %s has no tab character.' % (l, fname)
  399.                 continue
  400.             
  401.             (puzzle, diff) = l.split('\t')
  402.             if new and puzzle in self.played:
  403.                 continue
  404.             
  405.             if not sudoku.is_valid_puzzle(puzzle):
  406.                 print 'WARNING: invalid puzzle %s in file %s' % (puzzle, fname)
  407.                 continue
  408.             
  409.             diff = float(diff)
  410.             closeness_to_target = abs(diff - difficulty)
  411.             if closest[0] > closeness_to_target:
  412.                 closest = (diff, puzzle)
  413.                 continue
  414.         
  415.         return (closest[1], sudoku.SudokuRater(sudoku.sudoku_grid_from_string(closest[1]).grid).difficulty())
  416.  
  417.     
  418.     def n_puzzles(self, difficulty_category = None, new = True):
  419.         if not difficulty_category:
  420.             return []([ self.n_puzzles(c, new = new) for c in sudoku.DifficultyRating.categories ])
  421.         if self.n_available_sudokus[difficulty_category]:
  422.             return self.n_available_sudokus[difficulty_category]
  423.         lines = self.get_pregenerated(difficulty_category)
  424.         count = 0
  425.         for line in lines:
  426.             if not new or line.split('\t')[0] not in self.played:
  427.                 count += 1
  428.                 continue
  429.             self.n_available_sudokus[difficulty_category]
  430.         
  431.         self.n_available_sudokus[difficulty_category] = count
  432.         return self.n_available_sudokus[difficulty_category]
  433.  
  434.     
  435.     def list_puzzles(self, difficulty_category = None, new = True):
  436.         '''Return a list of all puzzles we have generated.
  437.         '''
  438.         puzzle_list = []
  439.         if not difficulty_category:
  440.             for c in sudoku.DifficultyRating.categories:
  441.                 puzzle_list.extend(self.list_puzzles(c, new = new))
  442.             
  443.         else:
  444.             lines = self.get_pregenerated(difficulty_category)
  445.             for l in lines:
  446.                 puzzle = l.split('\t')[0]
  447.                 if not new or puzzle not in self.played:
  448.                     puzzle_list.append(puzzle)
  449.                     continue
  450.             
  451.         return puzzle_list
  452.  
  453.     
  454.     def get_puzzles_random(self, n, levels, new = True, exclude = []):
  455.         '''Return a list of n puzzles and difficulty values (as floats).
  456.  
  457.         The puzzles will correspond as closely as possible to levels.
  458.         If new, we only return puzzles not yet played.
  459.         '''
  460.         if not n:
  461.             return []
  462.         if not levels:
  463.             raise AssertionError
  464.         puzzles = []
  465.         puzzles_by_level = { }
  466.         files = { }
  467.         for l in levels:
  468.             puzzles_by_level[l] = self.get_pregenerated(l)
  469.             random.shuffle(puzzles_by_level[l])
  470.         
  471.         i = 0
  472.         il = 0
  473.         n_per_level = { }
  474.         finished = []
  475.         while i < n and len(finished) < len(levels):
  476.             lev = levels[il]
  477.             if lev in finished:
  478.                 il += 1
  479.                 continue
  480.             
  481.             
  482.             try:
  483.                 line = puzzles_by_level[lev].pop()
  484.             except IndexError:
  485.                 finished.append(lev)
  486.  
  487.             
  488.             try:
  489.                 (p, d) = line.split('\t')
  490.             except ValueError:
  491.                 print 'WARNING: invalid line %s in file %s' % (line, files[lev])
  492.                 continue
  493.  
  494.             if sudoku.is_valid_puzzle(p):
  495.                 if p not in exclude:
  496.                     pass
  497.                 None if not new or p not in self.played else p not in self.played
  498.             else:
  499.                 print 'WARNING: invalid puzzle %s in file %s' % (p, files[lev])
  500.             il += 1
  501.         if i < n:
  502.             print 'WARNING: Not able to provide %s puzzles in levels %s' % (n, levels)
  503.             print 'WARNING: Generate more puzzles if you really need this many puzzles!'
  504.         
  505.         return puzzles
  506.  
  507.     
  508.     def get_puzzles(self, n, levels, new = True, randomize = True, exclude = []):
  509.         '''Return a list of n puzzles and difficulty values (as floats).
  510.  
  511.         The puzzles will correspond as closely as possible to levels.
  512.         If new, we only return puzzles not yet played.
  513.         '''
  514.         if randomize:
  515.             return self.get_puzzles_random(n, levels, new = new, exclude = exclude)
  516.         if not n:
  517.             return []
  518.         if not levels:
  519.             raise AssertionError
  520.         puzzles = []
  521.         files = { }
  522.         for l in levels:
  523.             files[l] = self.get_pregenerated(l)
  524.         
  525.         i = 0
  526.         il = 0
  527.         n_per_level = { }
  528.         finished = []
  529.         while i < n and len(finished) < len(levels):
  530.             lev = levels[il]
  531.             if lev in finished:
  532.                 il += 1
  533.                 continue
  534.             
  535.             if len(files[lev]) == 0:
  536.                 finished.append(lev)
  537.             else:
  538.                 line = files[lev][0]
  539.                 files[lev] = files[lev][1:]
  540.                 
  541.                 try:
  542.                     (p, d) = line.split('\t')
  543.                 except ValueError:
  544.                     print 'WARNING: invalid line %s in file %s' % (line, files[lev])
  545.                     continue
  546.  
  547.                 if sudoku.is_valid_puzzle(p):
  548.                     if p not in exclude:
  549.                         pass
  550.                     None if not new or p not in self.played else p not in self.played
  551.                 else:
  552.                     print 'WARNING: invalid puzzle %s in file %s' % (p, files[lev])
  553.             il += 1
  554.         if i < n:
  555.             print 'WARNING: Not able to provide %s puzzles in levels %s' % (n, levels)
  556.             print 'WARNING: Generate more puzzles if you really need this many puzzles!'
  557.         
  558.         return puzzles
  559.  
  560.     
  561.     def make_batch(self, diff_min = None, diff_max = None):
  562.         self.new_generator = InterruptibleSudokuGenerator(**self.generator_args)
  563.         key = self.new_generator.start_grid.to_string()
  564.         ug = self.new_generator.unique_generator(**self.puzzle_maker_args)
  565.         open_files = { }
  566.         for n in range(self.batch_size):
  567.             (puz, diff) = ug.next()
  568.             if not diff_min or diff.value >= diff_min:
  569.                 if not diff_max or diff.value <= diff_max:
  570.                     puzstring = puz.to_string()
  571.                     outpath = os.path.join(self.pickle_to, diff.value_category().replace(' ', '_'))
  572.                     existing = self.get_pregenerated(diff.value_category())
  573.                     if puzstring not in existing:
  574.                         
  575.                         try:
  576.                             outfi = file(outpath, 'a')
  577.                             outfi.write(puzstring + '\t' + str(diff.value) + '\n')
  578.                             outfi.close()
  579.                             self.n_available_sudokus[diff.value_category()] += 1
  580.                         except IOError:
  581.                             e = None
  582.                             print 'Error appending pregenerated puzzle: %s' % e.strerror
  583.                         except:
  584.                             None<EXCEPTION MATCH>IOError
  585.                         
  586.  
  587.                     None<EXCEPTION MATCH>IOError
  588.                     continue
  589.         
  590.  
  591.     
  592.     def pause(self, *args):
  593.         if hasattr(self, 'new_generator'):
  594.             self.new_generator.pause()
  595.         
  596.         self.paused = True
  597.  
  598.     
  599.     def resume(self, *args):
  600.         if hasattr(self, 'new_generator'):
  601.             self.new_generator.resume()
  602.         
  603.         self.paused = False
  604.  
  605.     
  606.     def stop(self, *args):
  607.         if hasattr(self, 'new_generator'):
  608.             self.new_generator.terminate()
  609.         
  610.         self.terminated = True
  611.  
  612.     
  613.     def hesitate(self):
  614.         while self.paused:
  615.             if self.terminated:
  616.                 break
  617.             
  618.             time.sleep(1)
  619.  
  620.     
  621.     def work(self, limit = None, diff_min = None, diff_max = None):
  622.         '''Intended to be called as a worker thread, make puzzles!'''
  623.         self.terminated = False
  624.         if hasattr(self, 'new_generator'):
  625.             self.new_generator.termintaed = False
  626.         
  627.         self.paused = False
  628.         generated = 0
  629.         while not limit or generated < limit:
  630.             if self.terminated:
  631.                 break
  632.             
  633.             if self.paused:
  634.                 self.hesitate()
  635.             
  636.             
  637.             try:
  638.                 self.make_batch(diff_min = diff_min, diff_max = diff_max)
  639.             except:
  640.                 raise 
  641.                 continue
  642.  
  643.             generated += 1
  644.  
  645.     
  646.     def get_difficulty(self, puz):
  647.         return sudoku.SudokuRater(puz).difficulty()
  648.  
  649.  
  650. if __name__ == '__main__':
  651.     import time
  652.     sm = SudokuMaker()
  653. elif False:
  654.     usage = 'Commands are:\n    run: Run sudoku-maker\n    len: \\# of puzzles generated\n    pause: pause\n    resume: resume\n    terminate: kill thread\n    quit: to quit\n    '
  655.     print usage
  656.     while None:
  657.         inp = raw_input('choose:')
  658.         if inp == 'run':
  659.             t = threading.Thread(target = sm.make_batch)
  660.             t.start()
  661.         elif inp == 'show':
  662.             print sm.puzzles
  663.         elif inp == 'len':
  664.             print len(sm.puzzles)
  665.         elif inp == 'pause':
  666.             sm.new_generator.pause()
  667.         elif inp == 'resume':
  668.             sm.new_generator.resume()
  669.         elif inp == 'terminate':
  670.             sm.new_generator.terminate()
  671.         elif inp == 'quit':
  672.             sm.new_generator.terminate()
  673.             break
  674.         else:
  675.             
  676.             try:
  677.                 getattr(sm, inp)()
  678.             except:
  679.                 print usage
  680.  
  681.         continue
  682. __name__ == '__main__'
  683.